<?php

namespace app\bll;

use app\lib\installer\General as LibInstallerGeneral;
use core\lib\tool\File;
use core\lib\upload\UploadFile as LibUploadFile;
use Exception;
use hulang\tool\FileHelper;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Melihovv\Base64ImageDecoder\Base64ImageEncoder;
use PhpZip\ZipFile;
use support\Db;
use Throwable;
use Webman\Event\Event;

class ComponentExtend
{

    public function find(int $id): array
    {
        $data =  Db::table('frame_component_extend')
            ->where('id', '=', $id)
            ->first();
        return $data === null ? [] : get_object_vars($data);
    }

    /**
     * 获取所有的扩展
     *
     * @return array
     */
    public function select(?int $groupid = null): array
    {
        return Db::table('frame_component_extend')
            ->when($groupid != null, function ($query) use ($groupid) {
                $query->where('groupid', '=', $groupid);
            })
            ->get()
            ->map(function ($value) {
                return (array)$value;
            })
            ->toArray();
    }

    public function install()
    {

        try {
            $libInstallerGeneral = new LibInstallerGeneral();
            $installres = $libInstallerGeneral->install(
                get_extend_path(),
                ['describe.php']
            );
            //读写描述文件写入数据库
            $describe   = $this->getDescribe($installres['foldername']);
            $extendmethod = $describe['method'];
            unset($describe['method']);

            Db::beginTransaction();
            try {
                $extendid = Db::table('frame_component_extend')
                    ->insertGetId($describe);
                foreach ($extendmethod as $key => $method) {
                    $extendmethod[$key]['extendid'] = $extendid;
                }
                Db::table('frame_component_extend_method')
                    ->insert($extendmethod);

                Db::commit();
            } catch (Exception $ex) {
                Db::rollBack();
                throw $ex;
            }
        } catch (Exception $ex) {
            throw $ex;
        }
    }


    /**
     * 手动安装
     *
     * @return void
     * @throws Exception
     */
    public function handleUpgrade(int $id)
    {
        Db::beginTransaction();
        try {
            $extend = $this->find($id);

            $libInstallerGeneral = new LibInstallerGeneral();
            $res = $libInstallerGeneral->offlineUpgrade(
                $extend['ident'],
                $extend['version'],
                get_extend_path(),
                ['describe.php']
            );

            $describe                = $this->getDescribe($extend['ident']);
            $extendmethod              = $describe['method'];
            $describe['nextversion'] = '';
            $describe['version']     = $res['nextVersion'];
            unset($describe['method']);

            Db::table('frame_component_extend')
                ->where('id', '=', $id)
                ->update($describe);

            Db::table('frame_component_extend_method')
                ->where('extendid', '=', $id)
                ->delete();

            foreach ($extendmethod as $key => $method) {
                $extendmethod[$key]['extendid'] = $id;
            }
            Db::table('frame_component_extend_method')
                ->insert($extendmethod);

            Db::commit();
        } catch (Exception $ex) {
            Db::rollBack();
            throw $ex;
        }
    }

    /**
     * 在线升级
     *
     * @param integer $id
     * @return void
     */
    public function onlineUpgrade(int $id)
    {
        $extend = $this->find($id);
        if (! $extend) {
            throw new Exception('扩展不存在');
        }

        if (trim($extend['upgrade']) == '') {
            throw new Exception('扩展没有升级地址');
        }

        Db::beginTransaction();
        try {
            $upgrade = $extend['upgrade'];
            if ($upgrade != '') {
                $libInstallerGeneral = new LibInstallerGeneral();
                $res = $libInstallerGeneral->onlineUpgrade(
                    $upgrade,
                    $extend['version'],
                    get_extend_path(),
                    ['describe.php']
                );
                $describe                = $this->getDescribe($extend['ident']);
                $describe['nextversion'] = '';
                $describe['version']     = $res['nextVersion'];
                $extendmethod            = $describe['method'];
                unset($describe['method']);

                Db::table('frame_component_extend')
                    ->where('id', '=', $id)
                    ->update($describe);

                Db::table('frame_component_extend_method')
                    ->where('extendid', '=', $id)
                    ->delete();

                foreach ($extendmethod as $key => $method) {
                    $extendmethod[$key]['extendid'] = $id;
                }
                Db::table('frame_component_extend_method')
                    ->insert($extendmethod);
            }
            Db::commit();
        } catch (Exception $ex) {
            Db::rollBack();
            throw $ex;
        }
    }



    /**
     * 获取描述文件
     *
     * @param string $path
     * @return array
     */
    public function getDescribe(string $foldername): array
    {

        $describe = include get_extend_path() . $foldername . '/describe.php';
        $describe['ident'] = $foldername;
        $logoimg        = get_extend_path() . $foldername . '/logo.png';
        if (file_exists($logoimg)) {
            $imageEncoder = Base64ImageEncoder::fromFileName($logoimg,  ['jpeg', 'png', 'gif']);

            $describe['logo'] =  $imageEncoder->getDataUri();
        }

        $bllComponentExtendGroup = new ComponentExtendGroup();
        $groupinfo               = $bllComponentExtendGroup->findByInterface($describe['group']);
        if ($groupinfo) {
            $describe['groupid'] = $groupinfo['id'];
        } else {
            $describe['groupid'] = 0;
        }
        unset($describe['group']);
        return $describe;
    }


    /**
     * 卸载
     *
     * @param integer $id
     * @return void
     */
    public function unInstall(int $id)
    {

        Db::beginTransaction();
        try {
            $extend = $this->find($id);
            $extendpath = get_extend_path() . $extend['ident'];


            //删除扩展表
            Db::table('frame_component_extend')
                ->where('id', '=', $id)
                ->delete();

            Db::table('frame_component_extend_method')
                ->where('extendid', '=', $id)
                ->delete();

            //处理消息通道
            Db::table('frame_component_extend_method')
                ->where('extendid', '=', $id)
                ->update([
                    'extend' => '',
                    'extendid' => 0,
                    'methodid' => 0,

                ]);
            //移除文件夹
            FileHelper::delDir($extendpath);

         
            Db::commit();
        } catch (Throwable $ex) {
            Db::rollBack();
            throw $ex;
        }
    }


    /**
     * 检查升级
     *
     * @param integer|array $extend_or_id
     * @return boolean
     */
    public function checkUpgrade(int|array $extend_or_id): bool
    {
        $extend = $extend_or_id;
        if (is_int($extend_or_id)) {
            $extend = $this->find($extend_or_id);
        }
        $upgrade = $extend['upgrade'];
        if ($upgrade == '') {
            return false;
        }

        $libInstallerGeneral = new LibInstallerGeneral();
        $nextVersion = $libInstallerGeneral->getNextUpgradeVersion($upgrade, $extend['version']);

        if ($nextVersion) {

            Db::table('frame_component_extend')
                ->where('id', '=', $extend['id'])
                ->update([
                    'nextversion' => $nextVersion['nextVersion']
                ]);
            return true;
        }

        Db::table('frame_component_extend')
            ->where('id', '=',  $extend['id'])
            ->update([
                'nextversion' => ''
            ]);
        return false;
    }

    /** 检查所有扩展是否有版本更新
     *
     * @return void
     */
    public function checkAllUpgrade()
    {
        $extends = $this->select();
        foreach ($extends as $extend) {
            $this->checkUpgrade($extend);
        }
    }


    /**
     * extend 层级关系
     *
     * @param boolean $isjson
     * @return string|array
     */
    public function cascader(?int $groupid = null, bool $isjson = true): string|array
    {

        $events = $this->select(groupid: $groupid);
        $bllComponentExtendMethod = new ComponentExtendMethod();
        $lists = $bllComponentExtendMethod->select();
        foreach ($events as $key => $event) {
            $events[$key]['cascaderid'] = $event['id'];
            foreach ($lists as $lkey => $list) {
                if ($list['extendid'] == $event['id']) {
                    $list['cascaderid'] = $event['id'] . '_' . $list['id'];
                    $events[$key]['children'][] = $list;
                    unset($lists[$lkey]);
                }
            }
        }

        return $isjson ? json_encode($events, JSON_UNESCAPED_UNICODE) : $events;
    }

    /**
     * 分页查询
     *
     * @param string $title
     * @param string $ident
     * @param integer $pagesize
     * @return LengthAwarePaginator
     */
    public function paginator(
        string $title = '',
        string $ident = '',
        ?int $groupid = null,
        int $pagesize = 20
    ): LengthAwarePaginator {
        return  Db::table('frame_component_extend')
            ->when($title != '', function ($query) use ($title) {
                return $query->where('title', 'like', '%' . $title . '%');
            })
            ->when($ident != '', function ($query) use ($ident) {
                return $query->where('ident', 'like', '%' . $ident . '%');
            })
            ->when($groupid != null, function ($query) use ($groupid) {
                return $query->where('groupid', '=', $groupid);
            })
            ->orderBy('id', 'desc')
            ->paginate($pagesize);
    }
}
